package com.by.product.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.by.common.exception.ErrorCode;
import com.by.common.redis.RedisKeyUtils;
import com.by.order.api.service.IOrderService;
import com.by.order.api.vo.OrderVo;
import com.by.product.api.service.IProductService;
import com.by.product.api.vo.ProductVo;
import com.by.product.entity.ProductEntity;
import com.by.product.entity.ProductStockLogEntity;
import com.by.product.mapper.ProductMapper;
import com.by.product.mapper.ProductStockLogMapper;
import com.by.rpc.annotation.RpcReference;
import com.by.rpc.annotation.RpcService;
import com.by.rpc.exception.BusinessException;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Slf4j
@Service
@RpcService(interfaceClass = IProductService.class, version = "1.0.0", group = "default")
public class ProductServiceImpl implements IProductService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductStockLogMapper productStockLogMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @RpcReference(interfaceClass = IOrderService.class, version = "1.0.0", group = "default")
    private IOrderService orderService;

    /**
     * 服务启动时，将所有商品库存预热到Redis。
     * 这是一个独立的初始化过程，不涉及复杂的业务逻辑或消息发送。
     */
    @PostConstruct
    public void initStock() {
        log.info("开始初始化商品库存到Redis...");
        try {
            List<ProductEntity> productEntities = productMapper.selectList(null);
            for (ProductEntity product : productEntities) {
                String stockKey = RedisKeyUtils.getSeckillStockKey(product.getId());
                redisTemplate.opsForValue().set(stockKey, product.getStock());
                log.info("商品 {} 库存预热到Redis完成，库存数量: {}", product.getId(), product.getStock());
            }
            log.info("所有商品库存预热完成。");
        } catch (Exception e) {
            log.error("商品库存预热失败！请检查数据库连接和Redis配置。", e);
        }
    }

    /**
     * 根据ID获取商品信息。
     */
    @Override
    public ProductVo getProductById(Long id) {
        ProductEntity productEntity = productMapper.selectById(id);
        if (productEntity == null) {
            return null;
        }
        ProductVo productVo = new ProductVo();
        BeanUtils.copyProperties(productEntity, productVo);
        return productVo;
    }

    /**
     * 异步扣减数据库库存。
     * 核心：通过本地数据库记录和唯一索引实现幂等性。
     *
     * @param orderId 订单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void decreaseStock(Long orderId) {
        // 1. 幂等性检查：根据订单ID和操作类型查询本地的库存扣减记录。
        ProductStockLogEntity existingLog = productStockLogMapper.selectOne(
                new LambdaQueryWrapper<ProductStockLogEntity>()
                        .eq(ProductStockLogEntity::getOrderId, orderId)
                        .eq(ProductStockLogEntity::getOperationType, "DECREASE")
        );

        if (existingLog != null) {
            log.warn("订单 {} 的库存扣减操作已处理过，本次为重复调用，直接返回成功。", orderId);
            return; // 直接返回，实现幂等
        }

        // 2. 远程获取订单信息，这是必要的，因为我们需要知道扣减数量
        OrderVo order = orderService.getOrderById(orderId);
        if (order == null) {
            log.warn("订单 {} 不存在，无法执行库存扣减。", orderId);
            return;
        }

        Long goodsId = order.getGoodsId();
        Integer goodsCount = order.getGoodsCount();

        // 3. 执行扣减和日志记录，在一个事务中完成
        // 先扣减库存，使用乐观锁保证并发安全
        LambdaUpdateWrapper<ProductEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ProductEntity::getId, goodsId)
                .ge(ProductEntity::getStock, goodsCount)
                .setSql(String.format("stock = stock - %d", goodsCount));

        int rows = productMapper.update(null, updateWrapper);
        if (rows != 1) {
            log.error("数据库库存扣减失败，可能是超卖或并发问题。订单ID: {}，商品ID: {}", orderId, goodsId);
            throw new BusinessException(ErrorCode.GOODS_STOCK_INSUFFICIENT.getCode(), "数据库库存扣减失败");
        }

        // 4. 插入库存扣减日志，确保原子性
        ProductStockLogEntity logEntity = new ProductStockLogEntity();
        logEntity.setOrderId(orderId);
        logEntity.setProductId(goodsId);
        logEntity.setDeductionCount(goodsCount);
        logEntity.setOperationType("DECREASE");
        logEntity.setCreateTime(LocalDateTime.now());

        productStockLogMapper.insert(logEntity);

        log.info("成功扣减数据库库存。订单ID: {}，商品ID: {}，数量: {}", orderId, goodsId, goodsCount);
    }

    /**
     * 异步回滚数据库库存。
     * 核心：通过本地数据库记录和唯一索引实现幂等性。
     *
     * @param orderId 订单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackStock(Long orderId) {
        // 1. 幂等性检查：检查是否已存在回滚记录
        ProductStockLogEntity existingLog = productStockLogMapper.selectOne(
                new LambdaQueryWrapper<ProductStockLogEntity>()
                        .eq(ProductStockLogEntity::getOrderId, orderId)
                        .eq(ProductStockLogEntity::getOperationType, "ROLLBACK")
        );

        if (existingLog != null) {
            log.warn("订单 {} 的库存回滚操作已处理过，本次为重复调用，直接返回成功。", orderId);
            return;
        }

        // 2. 远程获取订单信息
        OrderVo order = orderService.getOrderById(orderId);
        if (order == null) {
            log.warn("订单 {} 不存在，无法执行库存回滚。", orderId);
            return;
        }

        // 3. 执行回滚逻辑
        Long goodsId = order.getGoodsId();
        Integer goodsCount = order.getGoodsCount();

        LambdaUpdateWrapper<ProductEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ProductEntity::getId, goodsId)
                .setSql(String.format("stock = stock + %d", goodsCount));

        int rows = productMapper.update(null, updateWrapper);
        if (rows != 1) {
            log.error("数据库库存回滚失败。订单ID: {}，商品ID: {}", orderId, goodsId);
            throw new BusinessException(ErrorCode.SYSTEM_BUSY.getCode(), "数据库库存回滚失败");
        }

        // 4. 插入回滚日志
        ProductStockLogEntity logEntity = new ProductStockLogEntity();
        logEntity.setOrderId(orderId);
        logEntity.setProductId(goodsId);
        logEntity.setDeductionCount(goodsCount);
        logEntity.setOperationType("ROLLBACK");
        logEntity.setCreateTime(LocalDateTime.now());

        productStockLogMapper.insert(logEntity);

        log.info("成功回滚数据库库存。订单ID: {}，商品ID: {}，数量: {}", orderId, goodsId, goodsCount);
    }

    /**
     * 确认库存扣减。这是一个可选方法。
     *
     * @param orderId 订单ID
     */
    @Override
    public void confirmStockDeduction(Long orderId) {
        log.info("收到订单支付成功消息，确认库存扣减已完成。订单ID: {}", orderId);
    }
}